/**
 * @file    hal_sleep.c
 * @author  Azolla (1228449928@qq.com)
 * @brief    
 * @version 0.1
 * @date    2024-04-09
 * 
 * @copyright Copyright (c) 2024
 * */
#include "soc/rtc.h"
#include "soc/soc_caps.h"
#include "esp_log.h" 
#include "esp_err.h"
#include "driver/rtc_io.h"
#include "driver/uart.h"
#if SOC_TOUCH_SENSOR_NUM > 0
#include "soc/sens_periph.h"
#include "driver/touch_pad.h"
#endif

#include "hal_sleep.h"

#define TAG  "hal_sleep"

#ifdef TAG
#define HAL_SLEEP_LOGI(format, ...)    ESP_LOGI(TAG, format, ##__VA_ARGS__)
#define HAL_SLEEP_LOGW(format, ...)    ESP_LOGW(TAG, format, ##__VA_ARGS__)
#define HAL_SLEEP_LOGE(format, ...)    ESP_LOGE(TAG, format, ##__VA_ARGS__)
#else
#define HAL_SLEEP_LOGI(format, ...)
#define HAL_SLEEP_LOGW(format, ...)
#define HAL_SLEEP_LOGE(format, ...)
#endif

#define SLEEP_EXT1_WAKEUP_ENABLE    1
#define SLEEP_TIMER_WAKEUP_ENABLE   0
#define SLEEP_GPIO_WAKEUP_ENABLE    0
#define SLEEP_GPIO_HOLD_ENABLE      0    // GPIO睡眠保持状态

#define SLEEP_WAKEUP_GPIO_LEVEL     ESP_GPIO_WAKEUP_GPIO_LOW    // GPIO唤醒电平

#define CONFIG_WAKEUP_GPIO_PAD      GPIO_NUM_0

int hal_sleep_wakeup_cause(void)
{
    switch (esp_sleep_get_wakeup_cause()) {
#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
    case ESP_SLEEP_WAKEUP_GPIO: {
        uint64_t wakeup_pin_mask = esp_sleep_get_gpio_wakeup_status();
        if (wakeup_pin_mask != 0) {
            int pin = __builtin_ffsll(wakeup_pin_mask) - 1;
            HAL_SLEEP_LOGI("Wake up from GPIO %d\n", pin);
        } 
        break;
    }
#endif 

#if SOC_PM_SUPPORT_EXT_WAKEUP
    case ESP_SLEEP_WAKEUP_EXT1: {
        uint64_t wakeup_pin_mask = esp_sleep_get_ext1_wakeup_status();
        if (wakeup_pin_mask != 0) {
            int pin = __builtin_ffsll(wakeup_pin_mask) - 1;
            printf("Wake up from GPIO %d\n", pin);
        } else {
            printf("Wake up from GPIO\n");
        }
        break;
    }
#endif // SOC_PM_SUPPORT_EXT_WAKEUP

#if SLEEP_TIMER_WAKEUP_ENABLE    
    case ESP_SLEEP_WAKEUP_TIMER: {
        HAL_SLEEP_LOGI("Wake up from timer. Time spent in deep sleep\n");
        break;
    }
#endif    
    case ESP_SLEEP_WAKEUP_UNDEFINED:
    default:
        HAL_SLEEP_LOGW("Not a deep sleep reset\n");
        return -1;
    }
#if SLEEP_GPIO_HOLD_ENABLE 
    gpio_hold_dis(CONFIG_GPIO_PWOER_EN);  // 深度睡眠保持GPIO状态
    gpio_deep_sleep_hold_dis();
#endif    
    return 1;  // 唤醒
}
 
void hal_sleep_enter(void)
{
#if SLEEP_TIMER_WAKEUP_ENABLE    
    const int wakeup_time_sec = 3;
    // printf("Enabling timer wakeup, %ds\n", wakeup_time_sec);
    esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000);
#endif

#if (SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP && SLEEP_GPIO_WAKEUP_ENABLE)
    const gpio_config_t config = {
        .pin_bit_mask = BIT64(CONFIG_WAKEUP_GPIO_PAD),
        .intr_type = GPIO_INTR_ANYEDGE, 
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = true,
    };
    ESP_ERROR_CHECK(gpio_config(&config));
    ESP_ERROR_CHECK(esp_deep_sleep_enable_gpio_wakeup(BIT64(CONFIG_WAKEUP_GPIO_PAD), SLEEP_WAKEUP_GPIO_LEVEL));
    HAL_SLEEP_LOGI("Enabling GPIO wakeup on pins GPIO %d\n", SLEEP_WAKEUP_GPIO_LEVEL);
#endif

#if (SOC_PM_SUPPORT_EXT_WAKEUP && SLEEP_EXT1_WAKEUP_ENABLE) 
    const int ext_wakeup_pin1 = CONFIG_WAKEUP_GPIO_PAD;
    HAL_SLEEP_LOGI("Enabling EXT1 wakeup on pins GPIO: %d", ext_wakeup_pin1);
    esp_sleep_enable_ext1_wakeup(BIT64(ext_wakeup_pin1), ESP_EXT1_WAKEUP_ANY_LOW);
#endif // CONFIG_EXAMPLE_EXT1_WAKEUP

/**********************************************************************
在 Deep-sleep 模式中：
    数字 GPIO (GPIO6 ~ 21) 处于高阻态。
    RTC  GPIO (GPIO0 ~ 5) 可能处于以下状态：
    如果未启用保持 (hold) 功能，RTC GPIO 将处于高阻态。
    如果启用保持功能，RTC GPIO 管脚将会在保持功能开启时处于锁存状态。
*********************************************************************/
#if SLEEP_GPIO_HOLD_ENABLE
    gpio_set_level(CONFIG_GPIO_PWOER_EN, 1);
    gpio_hold_en(CONFIG_GPIO_PWOER_EN);  // 深度睡眠保持GPIO状态
    gpio_deep_sleep_hold_en();
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#endif

    uart_wait_tx_idle_polling(CONFIG_ESP_CONSOLE_UART_NUM);
    
    esp_deep_sleep_start();
}

